﻿using BulletSharp;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using DVector3 = BulletSharp.Math.Vector3;
using DMatrix = BulletSharp.Math.Matrix;

namespace ETModel {
    [ObjectSystem]
    public class BulletCollisionObjectAwakeSystem : AwakeSystem<BulletCollisionObject, float, Vector3, Quaternion> {
        public override void Awake(BulletCollisionObject self, float mass, Vector3 startPos, Quaternion startRot) {
            self.Awake(mass, startPos, startRot);
        }
    }
    public static class BulletCollisionObjectExtends {
        /// <summary>
        /// 获取物理对象组件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collObj"></param>
        /// <returns></returns>
        public static BulletCollisionObject GetBulletCollisionObject(this CollisionObject collObj) {
            var userObj = collObj.UserObject as BulletCollisionObject;
            if (userObj == null) {
                Log.Warning("有物理对象没走BulletCollisionObject组件!");
            }
            return userObj;
        }
        /// <summary>
        /// 获取物理对象的所属单位
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collObj"></param>
        /// <returns></returns>
        public static T GetParentEntity<T>(this CollisionObject collObj) where T : Entity {
            return collObj.GetBulletCollisionObject()?.GetParentEntity<T>();
        }
    }
    /// <summary>
    /// 物理对象组件<para/>
    /// AddComponent&lt;BulletCollisionObject, float, Vector3, Quaternion&gt;(mass,startPos,startRot)
    /// </summary>
    public class BulletCollisionObject : Component, IDisposable {

        public RigidBody RigidBody;
        public BulletCollisionObjectMotionState MotionState;

        /// <summary>
        /// 默认构造对象(刚体+加入DiscreteDynamicsWorld)
        /// </summary>
        public void Awake(float mass, Vector3 startPos, Quaternion startRot) {

            

            //todo:zum:怎么创建物理对象,需要设计一下,提供自定义出去
            //CollisionObjectTypes objType, 
            //CollisionFlags

            var parent = BulletHelper.ComponentAwakeCheck(this);
            var shapeCp = parent.GetComponent<BulletCollisionShape>();
            if (shapeCp == null) {
                throw new Exception("需要先加入形状组件(BulletCollisionShape)才能加对象组件(BulletCollisionObject)");
            }
            var shape = shapeCp.Shape;
            if (shape == null) {
                throw new Exception("BulletCollisionShaped初始化错误,没传入具体的形状");
            }

            //有重力则认为是动态,需要模拟重力
            bool isDynamic = (mass != 0.0f);

            DVector3 localInertia = DVector3.Zero;
            if (isDynamic)
                shape.CalculateLocalInertia(mass, out localInertia);
            var stratWorldTransform = Matrix4x4.TRS(startPos, startRot, Vector3.one).ToBullet();
            //使用运动状态对象,可监视和插值获取运动参数
            MotionState = new BulletCollisionObjectMotionState(stratWorldTransform);
            using (RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, MotionState, shape, localInertia)) {
                RigidBody = new RigidBody(rbInfo);
            }
            RigidBody.UserObject = this;

            (BulletCollisionWorld.Instance.World as DiscreteDynamicsWorld)?.AddRigidBody(RigidBody);

        }

        public override void Dispose() {

            if (RigidBody != null) {
                RigidBody.MotionState?.Dispose();
                (BulletCollisionWorld.Instance.World as DiscreteDynamicsWorld)?.RemoveRigidBody(RigidBody);
                RigidBody.Dispose();
                RigidBody = null;
                MotionState = null;
            }

            base.Dispose();
        }


        public Matrix4x4 GetWorldTransform() {
            return MotionState.GetWorldTrans().ToU3D();
        }
        private DMatrix _motionStateWorldTransform = new DMatrix();
        public void SetWorldTransform(ref Matrix4x4 matrix4x4) {
            matrix4x4.ToBullet(ref _motionStateWorldTransform);
            MotionState.SetWorldTrans(ref _motionStateWorldTransform);
            //todo:zum:测试设置物理对象的矩阵
            RigidBody.WorldTransform = _motionStateWorldTransform;
        }
        public void SetWorldTransform(Matrix4x4 matrix4x4) {
            matrix4x4.ToBullet(ref _motionStateWorldTransform);
            MotionState.SetWorldTrans(ref _motionStateWorldTransform);
            //todo:zum:测试设置物理对象的矩阵
            RigidBody.WorldTransform = _motionStateWorldTransform;
        }

        public Vector3 GetWorldPosition() {
            return MotionState.GetWorldTrans().GetU3DVector3();
        }
        public Quaternion GetRotation() {
            return MotionState.GetWorldTrans().GetU3DRotation();
        }


        /// <summary>
        /// 射线检测其他碰撞物体,返回物体所在的Entity
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public T RayTest<T>(Vector3 from, Vector3 to) where T : Entity {
            ClosestRayResultCallback callback;
            return RayTest<T>(from, to, out callback);
        }
        /// <summary>
        /// 射线检测其他碰撞物体,返回物体所在的Entity,并返回碰撞的信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public T RayTest<T>(Vector3 from, Vector3 to, out ClosestRayResultCallback callback) where T : Entity {
            var dFrom = from.ToBullet();
            var dTo = to.ToBullet();
            callback = new ClosestRayResultCallback(ref dFrom, ref dTo);
            BulletCollisionWorld.Instance.World.RayTest(dFrom, dTo, callback);
            if (callback.HasHit) {
                var unit = callback.CollisionObject.GetParentEntity<T>();
                return unit;
            }
            return null;
        }

        /// <summary>
        /// 获取所属的实体对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetParentEntity<T>() where T : Entity {
            return GetParent<T>();
        }
    }
}
